﻿using System;
using System.Collections.Generic;
using Garfield.Core;
using Garfield.Core.Runtime;
using Garfield.Core.Runtime.SPI;
using PicoContainer;
using PicoContainer.Defaults;

namespace Garfield.Runtime.Pico
{
    internal class ExtensionRegistry : IExtensionRegistry
    {
        private readonly PlatformProvider provider;
        private readonly DefaultPicoContainer serviceContext;
        private readonly IDictionary<object, ExtensionPoint> extensionPoints;

        public ExtensionRegistry(PlatformProvider provider, DefaultPicoContainer context)
        {
            this.provider = provider;
            serviceContext = context;
            extensionPoints = new Dictionary<object, ExtensionPoint>();
        }

        public void RegisterExtensionPoint(object identifier, bool isCaching)
        {
            if (extensionPoints.ContainsKey(identifier)) throw new ExtensionPointException("Extenion Point " + identifier + " is already registered");
            extensionPoints[identifier] = identifier.Equals(Platform.Services.Identifier) ? new ExtensionPoint(serviceContext) : new ExtensionPoint(CreateExtensionPoint(isCaching));
        }

        public void RegisterExtensionPoint<IdentifierType, ExtensionType>(object identifier, bool isCaching)
        {
            if (extensionPoints.ContainsKey(identifier)) throw new ExtensionPointException("Extenion Point " + identifier + " is already registered");
            extensionPoints[identifier] = identifier.Equals(Platform.SERVICES) ? new ExtensionPoint<IdentifierType, ExtensionType>(serviceContext) : new ExtensionPoint<IdentifierType, ExtensionType>(CreateExtensionPoint(isCaching));
        }

        public void RegisterExtensionPoint<ExtensionType>(object identifier, bool isCaching)
        {
            if (extensionPoints.ContainsKey(identifier)) throw new ExtensionPointException("Extenion Point " + identifier + " is already registered");
            extensionPoints[identifier] = new ExtensionPoint<ExtensionType>(CreateExtensionPoint(isCaching));
        }


        public ExtensionPoint GetExtensions(object extensionPointIdentifier)
        {
            if (!extensionPoints.ContainsKey(extensionPointIdentifier)) throw new ExtensionPointException("Extenion Point " + extensionPointIdentifier + " is not found");
            return extensionPoints[extensionPointIdentifier];
        }

        public void RegisterExtension(string extensionPointIdentifier, object extensionIdentifier, object extensionInstance)
        {
            GetExtensions(extensionPointIdentifier).RegisterExtension(extensionIdentifier, extensionInstance);
        }

        public void RegisterExtension(string extensionPointIdentifier, object extensionIdentifier, Type extensionType, Delegate initializer)
        {
            GetExtensions(extensionPointIdentifier).RegisterExtension(extensionIdentifier, extensionType, initializer);
        }

        public IExtensionPoint<ExtensionType> GetExtensions<ExtensionType>(Plugin.ExtensionPoint<ExtensionType> extensionPoint)
        {
            return (IExtensionPoint<ExtensionType>) GetExtensions(extensionPoint.Identifier);
        }

        public IExtensionPoint<ExtensionIdentifierType, ExtensionType> GetExtensions<ExtensionIdentifierType, ExtensionType>(Plugin.ExtensionPoint<ExtensionIdentifierType, ExtensionType> extensionPoint)
        {
            return (IExtensionPoint<ExtensionIdentifierType, ExtensionType>) GetExtensions(extensionPoint.Identifier);
        }

        public IExtensionPoint<Type, object> GetExtensions(Plugin.ExtensionPoint<Api, Implementation> extensionPoint)
        {
            return (IExtensionPoint<Type, object>) GetExtensions(extensionPoint.Identifier);
        }

        public void RegisterExtensionPoint(string identifier, bool isCachingExtension, Type extensionPointType)
        {
            if (extensionPoints.ContainsKey(identifier)) throw new ExtensionPointException("Extenion Point " + identifier + " is already registered");
            extensionPoints[identifier] = (ExtensionPoint)Activator.CreateInstance(extensionPointType, identifier.Equals(Platform.SERVICES) ? serviceContext : CreateExtensionPoint(isCachingExtension));
        }

        private DefaultPicoContainer CreateExtensionPoint(bool isCachingExtension)
        {
            return new DefaultPicoContainer(new ComponentAdapterFactory(provider, isCachingExtension)) { Parent = serviceContext };
        }
    }
}